package com.example.springbootdemo.util;

import com.alibaba.fastjson.JSON;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * @author hh
 * @date 2022/2/28
 */
public class OkHttpUtils {

    private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
    private static volatile OkHttpClient okHttpClient = null;
    private static volatile Semaphore semaphore = null;
    private Map<String, String> headerMap;
    private Map<String, String> paramMap;
    private String url;
    private Request request;

    /**
     * 初始化okHttpClient，并且允许https访问
     */
    private OkHttpUtils() {
        if (okHttpClient == null) {
            synchronized (OkHttpUtils.class) {
                if (okHttpClient == null) {
                    TrustManager[] trustManagers = buildTrustManagers();
                    okHttpClient = new OkHttpClient.Builder()
                            .connectTimeout(15, TimeUnit.SECONDS)
                            .writeTimeout(20, TimeUnit.SECONDS)
                            .readTimeout(20, TimeUnit.SECONDS)
                            .sslSocketFactory(createSslSocketFactory(trustManagers), (X509TrustManager) trustManagers[0])
                            .hostnameVerifier((hostName, session) -> true)
                            .retryOnConnectionFailure(true)
                            .build();
                    // addHeader("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36");
                }
            }
        }
    }

    /**
     * 创建OkHttpUtils
     * @return OkHttpUtils
     */
    public static OkHttpUtils builder() {
        return new OkHttpUtils();
    }

    /**
     * 请求地址
     * @param url 请求地址
     * @return OkHttpUtils
     */
    public OkHttpUtils url(String url){
        this.url = url;
        return this;
    }

    /**
     * 添加参数
     * @param paramMap 请求参数map
     * @return OkHttpUtils
     */
    public OkHttpUtils param(Map<String, String> paramMap){
        this.paramMap = paramMap;
        return this;
    }

    /**
     * 添加请求头
     * @param headerMap 请求头map
     * @return OkHttpUtils
     */
    public OkHttpUtils headers(Map<String, String> headerMap) {
        this.headerMap = headerMap;
        return this;
    }

    /**
     * get请求
     * @return String
     */
    public OkHttpUtils get(){
        StringBuilder urlBuilder = new StringBuilder(url);
        if (Objects.nonNull(paramMap)){
            urlBuilder.append("?");
            try {
                for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                    urlBuilder.append(URLEncoder.encode(entry.getKey(),"UTF-8"))
                            .append("=")
                            .append(URLEncoder.encode(entry.getValue(),"UTF-8"))
                            .append("&");
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            urlBuilder.deleteCharAt(urlBuilder.length() - 1);
        }

        Request.Builder requestBuilder = new Request.Builder()
                .url(urlBuilder.toString());

        setHeaders(requestBuilder);

        request = requestBuilder.get().build();
        return this;
    }

    /**
     * post请求
     * @param isJsonPost 是否是json体参数
     * @return String
     */
    public OkHttpUtils post(boolean isJsonPost){
        RequestBody requestBody;
        if (isJsonPost){
            String jsonBody = "";
            if (Objects.nonNull(paramMap)){
                jsonBody = JSON.toJSONString(paramMap);
            }
            requestBody = RequestBody.create(jsonBody, MEDIA_TYPE);
        }else {
            FormBody.Builder formBuilder = new FormBody.Builder();
            if (Objects.nonNull(paramMap)){
                paramMap.forEach(formBuilder::add);
            }
            requestBody = formBuilder.build();
        }

        Request.Builder requestBuilder = new Request.Builder()
                .url(url)
                .post(requestBody);

        setHeaders(requestBuilder);

        request = requestBuilder.build();
        return this;
    }

    /**
     * 同步请求
     * @return String
     */
    public String sync() {
        try {
            Response response = okHttpClient.newCall(request).execute();
            if (response.isSuccessful()){
                return Objects.requireNonNull(response.body()).string();
            }else {
                throw new IOException("请求失败:" + response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 异步请求，有返回值
     * @return string
     */
    public String async(){
        StringBuilder builder = new StringBuilder();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                builder.append("请求出错：").append(e.getMessage());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                builder.append(Objects.requireNonNull(response.body()).string());
                getSemaphoreInstance().release();
            }
        });
        try {
            getSemaphoreInstance().acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return builder.toString();
    }

    /**
     * 异步请求，带有接口回调
     * @param callBack callBack
     */
    public void async(ICallBack callBack) {
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                callBack.onFailure(call, e.getMessage());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                assert response.body() != null;
                callBack.onSuccessful(call, Objects.requireNonNull(response.body()).string());
            }
        });
    }

    /**
     * 设置请求头
     * @param requestBuilder requestBuilder
     */
    private void setHeaders(Request.Builder requestBuilder) {
        if (Objects.nonNull(headerMap)) {
            headerMap.forEach(requestBuilder::addHeader);
        }
    }

    /**
     * 用于异步请求时，控制访问线程数，返回结果
     * @return Semaphore
     */
    private static Semaphore getSemaphoreInstance() {
        // 只能1个线程同时访问
        synchronized (OkHttpUtils.class) {
            if (semaphore == null) {
                semaphore = new Semaphore(0);
            }
        }
        return semaphore;
    }

    /**
     * 生成安全套接字工厂，用于https请求的证书跳过
     */
    private static SSLSocketFactory createSslSocketFactory(TrustManager[] trustAllCerts) {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("SSL");
            sc.init(null, trustAllCerts, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ssfFactory;
    }

    private static TrustManager[] buildTrustManagers() {
        return new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
        };
    }

    /**
     * 自定义一个接口回调
     */
    public interface ICallBack {
        void onSuccessful(Call call, String data);
        void onFailure(Call call, String errorMsg);
    }

}
